SPDX-FileCopyrightText: 2019 Julia Barnoin & Guillaume Charinet SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
The Art of the Fugue # Authors: [Julia & Guillaume] # Date: [28.11.2019] # Blender version: [2.8] # OS: [Windows 10] #
from random import random, choice
from mathutils import Vector, Matrix
from mathutils.noise import random, seed_set
import bpy, bmesh
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False, confirm=False)
context = bpy.context
scene = context.sceneMaking new materials:
Red = bpy.data.materials.new("Red")
Red.diffuse_color = (0.8, 0, 0.000869932, 1)
Blue = bpy.data.materials.new("Blue")
Blue.diffuse_color = (0.113655, 0.510074, 0.8, 1)
Green = bpy.data.materials.new("Green")
Green.diffuse_color = (0.165985, 0.8, 0.0555711, 1)will apply the choosen material to the generated geometry
def apply_mat(mat): obj = bpy.context.active_object
obj.data.materials.append(mat)use of the golden ratio as proportion ratio for scaling and placing the geometry
def fib(a): fibonaci = []
x = 0
y = 1
while x < a:print(x)
fibonaci.append(x)
x, y = y, x + yprint (fibonaci)
return fibonaci[4:]will create a rectangle with locations and dimensions data
def boite(Lx, Ly, Lz, Sx, Sy, Sz): bpy.ops.mesh.primitive_cube_add(size=1, location=(Lx, Ly, Lz))
bpy.context.object.dimensions = (Sx, Sy, Sz)
return bpy.context.selected_objects[0]def get_BoundBox(ob):returns the corners of the bounding box of an object in world coordinates
from mathutils import Vector
bpy.context.view_layer.update()
bbox_corners = [ob.matrix_world @ Vector(corner) for corner in ob.bound_box]
return bbox_cornersdef check_Collision(box1, box2):Check Collision of 2 Bounding Boxes box1 & box2 must be lists of Vectors, containg the edges of the bounding boxes
print(‘COLLISION CHECK’) print(‘\n[START] Check collision with:’)
x_max = max([e[0] for e in box1])
x_min = min([e[0] for e in box1])
y_max = max([e[1] for e in box1])
y_min = min([e[1] for e in box1])
z_max = max([e[2] for e in box1])
z_min = min([e[2] for e in box1])print(‘[INFO] Box1 min %.2f, %.2f, %.2f’ % (x_min, y_min, z_min)) print(‘[INFO] Box1 max %.2f, %.2f, %.2f’ % (x_max, y_max, z_max))
x_max2 = max([e[0] for e in box2])
x_min2 = min([e[0] for e in box2])
y_max2 = max([e[1] for e in box2])
y_min2 = min([e[1] for e in box2])
z_max2 = max([e[2] for e in box2])
z_min2 = min([e[2] for e in box2])print(‘[INFO] Box2 min %.2f, %.2f, %.2f’ % (x_min2, y_min2, z_min2)) print(‘[INFO] Box2 max %.2f, %.2f, %.2f’ % (x_max2, y_max2, z_max2))
Check max and min values of coordinates Here, values must be inferior or equal
isColliding = (
(
(x_max >= x_min2 and x_max <= x_max2)
or (x_min <= x_max2 and x_min >= x_min2)
or (x_max2 <= x_max and x_min2 >= x_min)
or (x_max <= x_max2 and x_min >= x_min2)
)
and (
(y_max >= y_min2 and y_max <= y_max2)
or (y_min <= y_max2 and y_min >= y_min2)
or (y_max2 <= y_max and y_min2 >= y_min)
or (y_max <= y_max2 and y_min >= y_min2)
)
and (
(z_max >= z_min2 and z_max <= z_max2)
or (z_min <= z_max2 and z_min >= z_min2)
or (z_max2 <= z_max and z_min2 >= z_min)
or (z_max <= z_max2 and z_min >= z_min2)
)
)if isColliding: print(‘[RESULT] Collision found!’, box1.name, ‘and’, box2.name) else: print(‘[RESULT] No collision found.’)
print(‘[END]\n’)
return isCollidingwill return collision check as True
def testCollision(a, b): collision = check_Collision(get_BoundBox(a), get_BoundBox(b))
if collision:
print("[RESULT] Collision found!", a.name, "and", b.name)
return collisionwill delete geometry
def deleteBoite(): bpy.ops.object.delete(use_global=False, confirm=False)will list all geometry that already exist
def list_all(): res = []
existing_objects = list(bpy.data.objects)
for obj in existing_objects:
res.append(obj)
return ressubject generation on the X axis
def make_subject_1(): fibonaci = fib(100) #
fibonaciX = fib(
20
) # Use of the fibonacci function for the localisation and dimmension data Lx = 0
res = []
while Lx <= 100: # Start of the main loop
Ly = choice(fibonaci) #
Lz = choice(fibonaci) #
Sx = choice(
fibonaciX
) # --------> pick a random number in the fibonacci sequence
Sy = choice(fibonaci) #
Sz = choice(fibonaci) #
if (
Lx == 0 or Lx > 50 and Lx < 60 or Lx > 70 and Lx < 80
): # condition to generate attachement points
if (
Ly + Sy < 89 and Lz + Sz < 89
): # condition to restrain geometry to our 100x100 cube
Sx = 21 # ---------> restrainng size of our attachement points
Sy >= 21 #
newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
apply_mat(Red)
if not Lx == 0 and not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else:
oldboite = newboite
res.append(oldboite)
Lx = Lx + Sx # count will advance as the next geometry localisation
print(Lx)
else:
if (
Ly + Sy < 89 and Lz + Sz < 89
): # condition to generate in between attachement points
Sx < 13 # ---------> restrainng size of our "notes"
newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
apply_mat(Red)
if not Lx == 0 and not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else:
oldboite = newboite
res.append(oldboite)
Lx = Lx + Sx # count will advance as the next geometry localisation
print(Lx)
return resdef make_inversion(
list, Axis, Sx, Sy, Sz
): # making an inversion of the previous generated subject so they can be intricated
res = []
bpy.ops.object.select_all(action="DESELECT")
for obj in list:
obj.select_set(state=True)
bpy.ops.object.duplicate_move()
trans = bpy.ops.transform.rotate
trans(value=3.14159, orient_axis=Axis)
bpy.ops.transform.resize(
value=(Sx, Sy, Sz)
) # making a mirror version of our subject
for obj in bpy.context.selected_objects:
res.append(obj)
return resdef place_inversion(
list, inversion, x, y, z
): # placing our inversion so it cannot collide with the existent geometry
bpy.ops.object.select_all(action="DESELECT")
count = 0
collision = True
print(list)
while collision: # Starting of the loop
count += 1
print("loop number", count)
collision = False
for C1 in list: #
for (
C2
) in (
inversion
): # ---------------> comparing existent geometry to the inversion
print("test:", C1.name, C2.name)
while testCollision(C1, C2): # checking collision
bpy.ops.object.select_all(action="DESELECT")
for obj in inversion:
obj.select_set(state=True)
bpy.ops.transform.translate(
value=(x, y, z)
) # if collision append the whole inversion will move again and again
collision = True
print(collision)
if collision:
break
if collision:
break
print("number of test loops", count, collision)def make_voice4(list): # generates voice 4 as subject 1 inversion
inversion = make_inversion(list, "Z", 1, 1, -1)
bpy.ops.transform.translate(
value=(0, 50, 0),
orient_type="GLOBAL",
orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
orient_matrix_type="GLOBAL",
constraint_axis=(True, False, False),
mirror=True,
use_proportional_edit=False,
proportional_edit_falloff="SMOOTH",
proportional_size=1,
use_proportional_connected=False,
use_proportional_projected=False,
)
place_inversion(list, inversion, 0, 0.5, 0.5)def make_subject_2(): # subject generation on the Y axis
existing_objects = list(bpy.data.objects)
fibonaci = fib(100) #
fibonaciX = fib(
20
) # Use of the fibonacci function for the localisation and dimmension data Ly = 0
res = []
while Ly <= 100: # Start of the main loop
Lx = choice(fibonaci) #
Lz = choice(fibonaci) #
Sx = choice(
fibonaci
) # --------> pick a random number in the fibonacci sequence
Sy = choice(fibonaciX) #
Sz = choice(fibonaci) #
if (
Ly == 0 or Ly > 50 and Ly < 60 or Ly > 70 and Ly < 80
): # condition to generate attachement points
if (
Lx + Sx < 89 and Lz + Sz < 89
): # condition to restrain geometry to our 100x100 cube
Sy = 21 # ---------> restrainng size of our attachement points
newboite = boite(Lx, Ly + (Sy / 2), Lz, Sx, Sy, Sz)
apply_mat(Blue)
if Ly == 0: # exeption for the starting attachement point
print("LOOP 1st CUBE")
collision = True
while (
collision
): # loop checking collision so new subject will not collide with other existing geometry
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Ly = (
Ly + Sy
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Ly)
else: # generating our others attachement points
print("LOOP 2")
if not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else:
collision = True
while (
collision
): # loop checking collision so new subject will not collide with other existing geometry
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Ly = (
Ly + Sy
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Ly)
else:
print("LOOP 3")
if (
Lx + Sx < 89 and Lz + Sz < 89
): # condition to generate in between attachement points
newboite = boite(Lx, Ly + (Sy / 2), Lz, Sx, Sy, Sz)
apply_mat(Blue)
if not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else:
collision = True
while (
collision
): # loop checking collision so new subject will not collide with other existing geometry
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Ly = (
Ly + Sy
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Ly)break
return resdef make_voice3(list): # generates voice 3 as subject 2 inversion
voices_2_4_1 = list_all()
inversion_2 = make_inversion(list, "Z", 1, 1, -1)
bpy.ops.transform.translate(
value=(75, 0, 0),
orient_type="GLOBAL",
orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
orient_matrix_type="GLOBAL",
constraint_axis=(True, False, False),
mirror=True,
use_proportional_edit=False,
proportional_edit_falloff="SMOOTH",
proportional_size=1,
use_proportional_connected=False,
use_proportional_projected=False,
)resizing as a augmentation in music theory
bpy.ops.transform.resize(
value=(0.80, 0.80, 0.80),
orient_type="GLOBAL",
orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
orient_matrix_type="GLOBAL",
mirror=True,
use_proportional_edit=False,
proportional_edit_falloff="SMOOTH",
proportional_size=1,
use_proportional_connected=False,
use_proportional_projected=False,
)
place_inversion(voices_2_4_1, inversion_2, 0.1, -0.1, 0.3)def make_subject_3(): # subject generation on the X axis
existing_objects = list(bpy.data.objects)
fibonaci = fib(100) #
fibonaciX = fib(
20
) # Use of the fibonacci function for the localisation and dimmension data Lx = 0
res = []
while Lx <= 100: # Start of the main loop
Ly = choice(fibonaci) #
Lz = choice(fibonaci) #
Sx = choice(
fibonaciX
) # --------> pick a random number in the fibonacci sequence
Sy = choice(fibonaci) #
Sz = choice(fibonaci) #
if (
Lx == 0 or Lx > 50 and Lx < 60 or Lx > 70 and Lx < 80
): # condition to generate attachement points
if (
Ly + Sy < 89 and Lz + Sz < 89
): # condition to restrain geometry to our 100x100 cube
Sx = 21 #
Sz >= 21 # ---------> restrainng size of our attachement points
newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
apply_mat(Green)
if Lx == 0: # exeption for the starting attachement point
print("LOOP 1st CUBE")
collision = True
while (
collision
): # loop checking collision so new subject will not collide with other existing geometry
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Lx = (
Lx + Sx
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Lx)break
else: # generating our others attachement points
print("LOOP 2")
if not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else: # loop checking collision so new subject will not collide with other existing geometry
collision = True
while collision:
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Lx = (
Lx + Sx
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Lx)
else:
print("LOOP 3")
if (
Ly + Sy < 89 and Lz + Sz < 89
): # condition to generate in between attachement points
newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
apply_mat(Green)
if not testCollision(
oldboite, newboite
): # checking collision so all are lincked by one of their faces
deleteBoite()
else: # loop checking collision so new subject will not collide with other existing geometry
collision = True
while collision:
collision = False
for obj in existing_objects:
print("test:", newboite, obj)
if testCollision(newboite, obj):
collision = True
deleteBoite()
break
print(collision)
if collision:
break
if not collision:
Lx = (
Lx + Sx
) # count will advance as the next geometry localisation
oldboite = newboite
res.append(oldboite)
print(Lx)
return resdef make_voice6(list): # generates voice 6 as subject 3 inversion
voices_2_4_1 = list_all()
inversion_2 = make_inversion(list, "Z", 1, 1, -1)
bpy.ops.transform.translate(
value=(0, 75, 0),
orient_type="GLOBAL",
orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
orient_matrix_type="GLOBAL",
constraint_axis=(True, False, False),
mirror=True,
use_proportional_edit=False,
proportional_edit_falloff="SMOOTH",
proportional_size=1,
use_proportional_connected=False,
use_proportional_projected=False,
)
place_inversion(voices_2_4_1, inversion_2, 0.1, -0.1, 0.3)Calling all our subject and response
subject1 = make_subject_1()
make_voice4(subject1)
subject2 = make_subject_2()
make_voice3(subject2)
subject3 = make_subject_3()
make_voice6(subject3)